大家好,我是 Yubin
這篇文章會介紹如何將 Fastify 作為後端,並整合前端網頁,由 Fastify App 作為前端網頁的 Host,實現 Full Stack 的專案。
前端使用 React,後端使用 Fastify 搭配
@fastify/static
為範例。
React 是一個 JavaScript 的函式庫,透過 React 我們可以輕鬆的設計出複雜的網頁畫面。
關於 React 的學習,可以參考官方的教學文件 或其他人的鐵人賽文章 (很多)。
我們可以透過 create-reat-app
來快速建立一個 React 專案。
這邊打算把前後端的程式放在同一個專案目錄底下,用
frontend
表示前端、backend
表示後端。
mkdir my-app
cd my-app
npx create-react-app frontend --template typescript
使用 --template typescript
選擇 TypeScript 的樣板。
等專案建立好,敲入以下指令就可以看到這個樣板預設的畫面:
cd frontend
npm start
會自動打開預設的瀏覽器,瀏覽 localhost:3000
,3000
是 React 開發伺服器預設使用的 Port。
這邊必須要知道的是,透過 create-react-app
產生的專案,會使用 react-scripts 做為主要的工具,其中有三個重要的 scripts 分別對應三種模式。
npm run start
開發模式,會把 React App 開起來,預設會聽在 localhost:3000
,在這個模式下,如果有改動程式碼,畫面會自動更新,有錯誤或警告訊息也會在 Console 看到。
同時會把 NODE_ENV
環境變數設為 development
。
npm run build
生產模式,會把 React App 最佳化並打包成一些靜態檔案,出來的靜態檔案會放在 build
目錄底下。此時的 app 是可以準備部屬上線的。
打包過程中,會把 NODE_ENV
環境變數設為 production
。
npm run test
測試模式,會執行測試檔案。
測試過程中,會把 NODE_ENV
環境變數測為 test
。
這邊假設我們已經把前端畫面開發完畢,敲入 npm run build
。
可以看到產生出來的 build
目錄,觀察裡面可以看到 index.html
就是網頁的進入點,然後各個 React Compoment 都被最佳化並打包成許多 JavaScript, CSS 檔放在 static
目錄中。
到此,我們只需要把 build
目錄,交給一個網頁伺服器,就可以讓網站上線了。
但本篇的主題是前後端整合,我們會使用 Fastify 當作我們的前端網頁的 Host。
在專案目錄中,建立 backend
目錄,用來放後端的程式。
cd my-app
mkdir backend
cd backend
npm init -y
這邊快速的建立一個基本的 Fastify App,還不熟 Fastify 的基本專案怎麼實作的朋友,可以參考 Fastify101: Hello World。
npm i fastify
npm i -D typescript @types/node
npx tsc --init
修改 tsconfig.json
:
"include": ["src/**/*.ts"],
"exclude": ["node_modules"],
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
}
新增 src/server.ts
:
import fastify, { FastifyInstance } from 'fastify'
const server: FastifyInstance = fastify()
const startServer: (port: number) => FastifyInstance = (port) => {
const listenAddress = '0.0.0.0'
const fastifyConfig = {
port: port,
host: listenAddress
}
server.listen(fastifyConfig, (error, _) => {
if (error) {
console.error(error)
}
})
server.get('/hello', async (request, reply) => {
return reply.status(200).send({
message: 'Hello World'
})
})
return server
}
export { startServer }
新增程式進入點 src/index.ts
:
import { startServer } from './server'
const port = 8888
startServer(port)
修改 package.json
中的 scripts:
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
以上完成一個基本的 Fastify 專案架構,可以編譯並執行:
npm run build && npm run start
打開瀏覽器,瀏覽 localhost:8888/hello
。
看到如我們定義的,顯示 Hello World 回應。
一個基本的 Fastify App 已經好了,我們的前端也 build 出準備部屬的靜態檔案了,現在要讓 Fastify App 可以 serve 這些靜態檔案。
講人話,就是別人發送 Request 給伺服器想看網頁,伺服器可以回應
.html
,.js
,.css
,.jpg
等靜態檔案出去。
這邊可以透過 Fastify 官方的 @fastify/static 來實現。
@fastify/static 是一個可以快速回應靜態檔案的 Fastify Plugin。
可以透過 npm 來安裝:
npm i @fastify/static
安裝好後透過 server.register()
註冊:
import fastifyStatic from '@fastify/static'
import path from 'path'
server.register(fastifyStatic, {
root: path.join(__dirname, '../../frontend/build'),
prefix: '/'
})
註冊的時候可以帶入一些參數。
root
,必要的參數,要指定要 serve 的目錄的絕對路徑。上述範例透過 path.join
及 __dirname
來指到 frontend 專案的 build
目錄。
prefix
,用來當 url 的前墜,預設為 /
。
更多參數可以參考官方的文件。
接著再次編譯並把後端專案起起來:
npm run build && npm run start
打開瀏覽器,瀏覽 localhost:8888
。
可以看到,原本我們的後端在 /
這個 Endpoint 沒有定義東西,應該要呈現 404
的回應,但我們透過 @fastify/static
這個 plugin 把前端網頁的靜態檔案 serve 起來,prefix
設定為 /
。所以可以瀏覽到前端的畫面。
本文的做法,是將前端專案 build 出靜態檔案,把那些檔案交由後端的伺服器來 Serve。
注意前端專案要先 build 完,產生要部屬的靜態檔案才有畫面。
部屬方面相對單純,因為只會有後端的伺服器,別人要看網頁或 API 資源都只要跟這個伺服器作互動就好。
在前端專案的程式碼中,如果要拿取後端的 API 資源,也只要透過相對路徑的方式就可以存取到。
因為前端最後會跟後端住在一起。
假設後端有一個 API 開在 GET /hello
上。
前端的程式可以像這樣用相對路徑的方式拿到資源,而不用管後端的 hostname 是什麼:
// 以 axios 為例
const response = await axios.get('/hello')
因為最後他們是住在一起的,是同個 server 上的東西。
本文介紹了利用 @fastify/static 來實現 Full Stack 專案的前後端整合架構。
本篇以 React 專案做解釋,但這個方法不限於 React 專案,只要最後 build 出來的產物是靜態檔案的都可以用這個方式整合。
完整範例程式可以參考 GitHub。